package com.threeti.mecool.core.domain.model.project;

import com.threeti.mecool.framework.domain.support.DefaultDomainEntity;
import com.threeti.mecool.framework.util.Lists;
import com.threeti.mecool.shared.util.TreeNodeWithChilds;
import org.apache.commons.lang.Validate;
import org.codehaus.jackson.annotate.JsonBackReference;
import org.codehaus.jackson.annotate.JsonIgnore;
import org.codehaus.jackson.annotate.JsonManagedReference;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;

import javax.persistence.*;
import java.util.Collections;
import java.util.List;

/**
 * 项目员工
 *
 * @author Jay Meng
 */
@Entity
@Table(name = "DM_PROJECT_EMPLOYEE")
public class ProjectEmployee extends DefaultDomainEntity implements TreeNodeWithChilds {

    @ManyToOne
    @JsonIgnore
    private Project project;

    private Long empId;
    private String empName;//jay:orm redundant copy for fast representation(i.e. tree representation)
    private String titleWithinTheProject;

    @Enumerated(EnumType.ORDINAL)
    private ProjectEmployeeRankType rankType = ProjectEmployeeRankType.LEVEL_EXECUTION;

    @ManyToMany
    @JoinTable(name = "DM_PROJECT_EMPLOYEE_STORE", joinColumns = {@JoinColumn(name = "PROJEMP_ID")}, inverseJoinColumns = {@JoinColumn(name = "PROJSTORE_ID")})
    //@Fetch(FetchMode.SUBSELECT)//(fetch=FetchType.EAGER)
    @JsonIgnore
    private List<ProjectStore> assignedStores = Lists.newArrayList();

    @ManyToOne
    @JsonManagedReference
    private ProjectEmployee parent;

    @OneToMany(mappedBy = "parent", cascade = {CascadeType.ALL}, orphanRemoval = true, fetch = FetchType.EAGER)
//jay:'mappedBy' plus 'ManyToOne'，可实现一张表管理父子关系和一对多的双向关联
    @JsonBackReference
    @Fetch(FetchMode.SUBSELECT)
    private List<ProjectEmployee> childs = Lists.newArrayList();

    public ProjectEmployee(Project project, Long empId) {
        this.empId = empId;
        this.project = project;
    }

    public ProjectEmployee(Project project, Long empId, String empName, String titleWithinTheProject, ProjectEmployeeRankType rankType) {
        this(project, empId);
        this.empName = empName;
        this.titleWithinTheProject = titleWithinTheProject;
        this.rankType = rankType;
    }

    /**
     * 员工编号(遗留系统)
     */
    public Long getEmpId() {
        return empId;
    }

    /**
     * 员工所参与的项目
     */
    public Project getProject() {
        return project;
    }

    /**
     * 取得项目中上级员工
     */
    public ProjectEmployee getParent() {
        return parent;
    }

    /**
     * 取得项目中的下属员工
     */
    public List<ProjectEmployee> getChilds() {
        return Collections.unmodifiableList(childs);
    }

    /**
     * 取得员工名
     */
    public String getEmpName() {
        return empName;
    }

    /**
     * 取得员工在项目中的岗位Title
     */
    public String getTitleWithinTheProject() {
        return titleWithinTheProject;
    }

    /**
     * 取得员工在项目中的岗位类别
     */
    public ProjectEmployeeRankType getRankType() {
        return rankType;
    }

    /**
     * 取得被分配给本督导的门店们
     */
    public List<ProjectStore> getAssignedStores() {
        return assignedStores;
    }

    /**
     * 为本督导批量分配门店
     */
    public ProjectEmployee assignStores(List<ProjectStore> stores) {
        Validate.notNull(stores, "分配的门店不能为空");
        setAssignedStores(stores);
        return this;
    }

    /**
     * 为本督导分配门店
     */
    public ProjectEmployee assignStore(ProjectStore store) {
        Validate.notNull(store, "分配的门店不能为空");
        Validate.notNull(store.getId(), "分配的门店必须已经存在");

        if (assignedStores.contains(store)) {
            throw new RuntimeException("无法分配,门店无法重复分配给督导" + store);
        }
        assignedStores.add(store);
        return this;
    }

    /**
     * 添加下级
     */
    public ProjectEmployee addChild(ProjectEmployee node) {
        node.setParent(this);
        childs.add(node);
        return this;
    }

    /**
     * 和已绑定的所有门店解除绑定关系
     */
    public ProjectEmployee unbindWithAllAssignedStores() {
        this.assignedStores = null;
        return this;
    }

    /**
     * 和已绑定的特定门店解除绑定关系
     */
    public ProjectEmployee unbindWithSpecifiedAssignedStore(List<ProjectStore> projStoresNeedToBeUnbinded) {
        if (this.assignedStores != null) this.assignedStores.removeAll(projStoresNeedToBeUnbinded);
        return this;
    }

    /**
     * 强制刷新门店的绑定
     */
    public void setAssignedStores(List<ProjectStore> assignedStores) {
        this.assignedStores = assignedStores;
    }

    public ProjectEmployee() {
        super();
    }

    @Override
    public String toString() {
        return "ProjectEmployee [project=" + project + ", empId=" + empId + ", empName=" + empName
                + ", titleWithinTheProject=" + titleWithinTheProject + ", rankType=" + rankType + ", parent=" + parent + "]";
    }

    public void setProject(Project project) {
        this.project = project;
    }

    public void setParent(ProjectEmployee parent) {
        this.parent = parent;
    }

    @Override
    public Object getParentId() {
        return parent.getEmpId();
    }

    @Override
    public String getName() {
        return getEmpName();
    }

}
